/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 三个状态常量
const PENDING = 'pending'  // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {
  constructor(executor) {
    try {
      // 立即执行函数
      executor(this.resolve, this.reject)
    } catch(e) {
      this.reject(e)
    }
  }
  // 添加状态 默认值为PENDING
  status = PENDING;

  // 实例，成功之后的值
  value = '';
  // 实例 失败之后的原因
  error = '';

  // 初始化为数组，可支持多个then调用
  successCallback = [];
  failedCallback = [];

  // 箭头函数 => 保证内部this指向Promise对象
  resolve = (val) => {
    // 如果状态不是等待，阻止程序执行（状态一旦确定不可更改）
    if(this.status !== PENDING) return;
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功之后的值
    this.value = val;

    // - 单个回调函数调用
    // 异步情况调用成功函数(先判断是否有successCallback)
    // this.successCallback && this.successCallback(this.value);

    // - 多个回调函数调用
    this.successCallback.forEach(fn => fn());
  }

  reject = (err) => {
    if(this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    this.error = err;
    
    // - 单个回调函数调用
    // 异步情况调用失败函数(先判断是否有failedCallback)
    // this.failedCallback && this.failedCallback(this.error);

    // - 多个回调函数调用
    this.failedCallback.forEach(fn => fn());
  }

  // then 方法传入两个回调函数
  then(successCallback, failedCallback) {
    // 判断回调一定是个函数 不是函数则无效
    successCallback = typeof successCallback === 'function' ? successCallback : val => val;
    failedCallback = typeof failedCallback === 'function' ? failedCallback : err => err;
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        /** 
         * 判断 x 的值是普通值还是primose对象
         * 如果是普通值 直接调用resolve
         * 如果是promise对象 查看promise对象返回的结果
         * 再根据promise对象返回的结果 决定调用resolve 还是调用reject
        */
        // tip: new MyPromise2是同步的 promise2还没赋值，所以这里要处理成异步
        setTimeout(() => {
          try {
            let x = successCallback(this.value); 
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) {
            reject(e)
          }
        })
       
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failedCallback(this.error);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) {
            reject(e)
          }
        })
      } else {
        /** 
         * 处理异步逻辑 等待状态
         * 将成功状态和等待状态存储起来
         * 为了处理异常情况 push一个函数
        */
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value); 
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e)
            }
          })
        });
        this.failedCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failedCallback(this.error);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e)
            }
          })
        });
      }
    });

    return promise2;
  }

  // catch方法其实就是then方法，不传入成功回调参数
  catch(failedCallback) {
    return this.then(null, failedCallback)
  }

  finally(callback) {
    // 不管成功还是失败都会执行
    return this.then((val) => {
      return MyPromise.resolve(callback()).then(() => value);
    }, (err) => {
      return MyPromise.resolve(callback()).then(() => { throw err });
    })
  }

  // 内部不允许调用 使用static 
  // 返回一个promise对象
  static all(array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData(i, data) {
        result[i] = data;
        if (++index === array.length) {
          // 保证循环的异步操作能正常执行
          resolve(result);
        }
      }

      for(let i = 0; i < array.length; i++) {
        let current = array[i];

        // 判断promise对象还是普通值
        if(current instanceof MyPromise) {
          current.then(val => addData(i, val), err => reject(err));
        } else {
          // 普通值
          addData(i, current);
        }
      }
    })
  }

  // 返回一个promise对象
  static race(array) {
    new MyPromise((resolve, reject) => {
      // 参数为空，则promise永远等待
      if (array.length === 0) return

      // 只要其中一个状态改变 promise的状态就改变
      for (let i = 0; i < array.length; i++) {
        MyPromise.resolve(promises[i]).then((data) => {
          resolve(data);
        }, (err) => {
          reject(err);
        });
      }
    })
  }

  // 返回一个以给定值解析后的 Promise 对象
  static resolve(value) {
    // promise 对象则原封不动返回该对象
    if(value instanceof MyPromise) return value;
    // 包裹成promise对象
    return new MyPromise((resolve,reject) => {
      resolve(value);
    })
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  // promise2 等于 x 说明产生了自身引用
  if (promise2 === x) {
    // 返回类型错误
    return reject(reject(new TypeError('Chaining cycle')))
  }
  // 判断 x 是不是Mypromise的实例，如果是则说明是MyPromise对象
  if (x instanceof MyPromise) {
    // promise 对象
    // 成功走第一个回调，失败走第二个回调
    x.then(resolve, reject);
  } else {
    resolve(x)
  }
}

module.exports = MyPromise;